class TLV
  @@EMV = 1
  @@DGI = 2
  attr_accessor :tag, :data, :type
  #
  #
  #
  def initialize(tag, bs, type)
    @tag = tag
    @type = type
    if(bs.kind_of?(ByteString))
      @data = bs.getBytes  
    elsif(bs.kind_of?(Array))
      @data = bs
    else
      raise(ArgumentError, "Second argument for initialize(tag,bs,type) must be ByteString or Array", caller) 
    end
  end 
  
  #
  #
  #
  def == (tlv)
    return true if tlv.equal?(self)
    return false unless tlv.kind_of?(self.class)
    @tag == tlv.tag && @type == tlv.type && @data = tlv.data
  end
  #
  #
  #
  def encodingMode
    return @type
  end
  #
  #
  #
  def self.EMV
    return @@EMV
  end
  
  #
  #
  #
  def self.DGI
    return @@DGI
  end
  
  #
  #
  #
  def getTag
    return @tag
  end
  
  #
  #
  #
  def getValue
    return ByteString.new(@data,HEX)
  end
  
  #
  #
  #
  def size
    return self.getTLV.length
  end
  
  #
  #
  #
  def getL 
    return ByteString.new(self.encode_length(),HEX);
  end
  
  #
  #
  #
  def getLV
    tmp = Array.new()
    tmp << self.encode_length << @data
    tmp.flatten!
    return ByteString.new(tmp,HEX)    
  end
  
  #
  #
  #
  def getTV
    tmp = Array.new();
    tmp << self.encode_tag << @data
    tmp.flatten!
    return ByteString.new(tmp,HEX)     
  end
  #
  #
  #
  def getTLV
    tmp = Array.new();
    tmp << self.encode_tag << self.encode_length << @data
    tmp.flatten!
    return ByteString.new(tmp,HEX);
  end
  #
  #
  #
  def encode_tag
    case @type 
    when TLV.EMV
        tag_size = self.get_tag_field_size()
        t = Array.new(tag_size)
        akku = @tag
        i = t.length - 1
        while i>=0 
            t[i] = (akku&0xFF)
            akku >>= 8
            i -= 1
        end
        return t;
    when TLV.DGI
        t = Array.new(2);
        t[0] = (@tag >> 8);
        t[1] = (@tag & 0xFF);
        return t;
    end
  end
  
  #
  #
  #
  def encode_length
    case @type 
    when TLV.EMV
        len = @data.length;
        size = self.get_length_field_size();
        i = 0;
        encodedLength = Array.new(size);
        
        offset = 0;
        if size > 1 then
            encodedLength[offset] = (0x80 | (size - 1));
            i = (size - 2) * 8;
            offset += 1;
        end

        while i >= 0
            encodedLength[offset] = (len >> i);
            offset += 1;
            i -= 8
        end
        return encodedLength;
    when TLV.DGI
        len = @data.length;
        size = self.get_length_field_size();
        encodedLength = Array.new(size);
        if size > 1
          encodedLength[0] = 0xFF;
          encodedLength[1] = (len >> 8);
          encodedLength[2] = (len & 0xFF);
        else
          encodedLength[0] = len;
        end
        return encodedLength;   
    end
  end

  #
  #
  #
  def get_length_field_size
    case @type
    when TLV.EMV
        size = 1
        size +=1 if @data.length >= 0x80
        size +=1 if @data.length >= 0x100
        size +=1 if @data.length >= 0x10000
        size =-1 if @data.length >= 0x1000000
        return size;      
    when TLV.DGI
        size = 1;
        size = 3 if @data.length >= 0xFF
        size = -1 if @data.length > 0xFFFF     
        return size;     
    end  
  end
  
  #
  #
  #
  def self.get_length_field_size_helper length,format
      case format
      when TLV.EMV
          size = 1
          size +=1 if length >= 0x80
          size +=1 if length >= 0x100
          size +=1 if length >= 0x10000
          size =-1 if length >= 0x1000000
          return size;      
      when TLV.DGI
          size = 1;
          size = 3  if length >= 0xFF
          size = -1 if length > 0xFFFF     
          return size;     
      end  
  end
  
  #
  #
  #
  def self.length_to_bytes len,size,format
    case format 
    when TLV.EMV
        i = 0;
        encodedLength = Array.new(size);
        
        offset = 0;
        if size > 1 then
            encodedLength[offset] = (0x80 | (size - 1));
            i = (size - 2) * 8;
            offset += 1;
        end

        while i >= 0
            encodedLength[offset] = (len >> i);
            offset += 1;
            i -= 8
        end
        return encodedLength;
    when TLV.DGI
        encodedLength = Array.new(size);
        if size > 1
          encodedLength[0] = 0xFF;
          encodedLength[1] = (len >> 8);
          encodedLength[2] = (len & 0xFF);
        else
          encodedLength[0] = len;
        end
        return encodedLength;   
    end    
  end
  
  #
  #
  #
  def get_tag_field_size
    case @type 
    when TLV.EMV
      size = 1;
      if @tag >= 0x01000000 then
          size = 4;
      elsif @tag >= 0x010000 then
          size = 3;
      elsif @tag >= 0x0100 then
          size = 2;
      else
          size = 1;
      end
      return size
    when TLV.DGI
      return 2;
    end
  end
end